Gradle 基础知识

本文将对Gradle的一些基本概念、用法进行总结,从而避免自己在Android开发过程中的“会而不知所以然”,关于Gradle的高阶用法可以见参考,本文就不再赘述;

基本概念

Wrapper

为了保证gradle版本的一致性,比如我们下载某个Android工程,编译运行前,wrapper会检查你本地是否存在gradle-wrapper.properties文件中声明的gradle版本,如果不存在,就会去下载,通常是个gradlew配合一起使用的,如下示例,可以帮助你更好的理解wrapper的概念;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
[min@TATEMIN-MB0:] gradle $ mkdir wrapper-demo  # 创建一个空的wrapper-demo
[min@TATEMIN-MB0:] gradle $ cd wrapper-demo/
[min@TATEMIN-MB0:] wrapper-demo $ gradle -v # 查看当前gradle的版本

------------------------------------------------------------
Gradle 5.4.1
------------------------------------------------------------

Build time: 2019-04-26 08:14:42 UTC
Revision: 261d171646b36a6a28d5a19a69676cd098a4c19d

Kotlin: 1.3.21
Groovy: 2.5.4
Ant: Apache Ant(TM) version 1.9.13 compiled on July 10 2018
JVM: 1.8.0_241 (Oracle Corporation 25.241-b07)
OS: Mac OS X 10.15.4 x86_64

[min@TATEMIN-MB0:] wrapper-demo $ gradle wrapper # 创建一个gradle wrapper

BUILD SUCCESSFUL in 0s
1 actionable task: 1 executed
[min@TATEMIN-MB0:] wrapper-demo $ tree
.
├── gradle
│   └── wrapper
│   ├── gradle-wrapper.jar
│   └── gradle-wrapper.properties
├── gradlew
└── gradlew.bat

2 directories, 4 files
# 查看wrapper的内容,可以看到distributionUrl的版本号和我们本地的对齐,此工程如果那运作在一台没有安装过gradle-5.4.1的设备上,会先去下载gradle-5.4.1对应的包,此过程由gradlew+gradle-wrapper.jar完成;
[min@TATEMIN-MB0:] wrapper-demo $ cat gradle/wrapper/gradle-wrapper.properties
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

GradleUserHome

在Mac/Linux系统上,此目录即为~/.gradle目录,可以理解为gradle的workspace。

Daemon

因为JVM启动速度很慢,大约每次启动在10s左右,所以gradle为了解决这个问题,将gradle设计为如下结构:

gradle-deamon

大体的意思即使说,在启动gradle后会同时启动两个进程,一个Client,一个Daemon,Client负责具体的交互,比如命令的接收、日志的打印等等,Daemon负责具体的编译工作并和Client完成交互,Client端推出后,Daemon并不立即退出,如果期间没有新的Client发消息进来,会存活3小时左右,这样就避免了每次gradle启动时等待JVM启动的耗时,另外如果当前启动的Client版本和Daemon不一致,会重新启动一个Deamon

Daemon 4.0以前,CI相关编译建议加上–no-daemon,因为此时Daemon并不稳定,后续版本无此问题。

Groovy基础

动态调用和MOP

MOP:元对象协议。由 Groovy 语言中的一种协议。该协议的出现为元编程提供了优雅的解决方案。而 MOP 机制的核心就是 MetaClass。
元编程:编写能够操作程序的程序,也包括操作程序自身,示例如下:

1
2
3
string = 'hello'
targetMethod = string.metaClass.getMetaMethod('toUpperCase')
println(targetMethod.invoke(string))

闭包

  • 闭包是一个开放,匿名的代码块,可以接受参数,返回值并分配给变量

  • 闭包定义:{ [closureParameters -> ] statements },示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    //执行一句话  
    { printf 'Hello World' }

    //闭包有默认参数it,且不用申明
    { println it }

    //闭包有默认参数it,申明了也无所谓
    { it -> println it }

    // name是自定义的参数名
    { name -> println name }

    //多个参数的闭包
    { String x, int y ->
    println "hey ${x} the value is ${y}"
    }
  • Android示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    def android(closure) = {
    closure()
    }

    def ext(closure) = {
    closure()
    }

    android {
    ext {
    println("i am ext")
    }
    }

Gradle 构建

主要理解gradle的核心模型

  • Project

    每个Gradle构建都由一个或多个Project组成。

  • Task

    每个Project由一个或多个Task组成,Task代表构建执行的一些原子工作,比如编译某些类、创建JAR、生成Javadoc等等;

  • Lifecycle与Hook

    Gradle构建的声明周期都分为三个不同的阶段.

    • Initialization

      Gradle支持单项目和多项目构建. 在初始化阶段,Gradle确定将要参与构建的Project,并为每个Project创建一个Project实例(构建文件与Gradle交互的主要API.)

    • Configuration

      在此阶段,将配置项目对象,执行作为构建一部分的所有项目的构建脚本.

    • Execution

      Gradle确定要在配置阶段创建和配置的任务子集. 子集由传递给gradle命令的任务名称参数和当前目录确定. 然后Gradle执行每个选定的任务.

下面是一个非常好的例子可以理解一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
task("hello_world") {
println("configuration")

doLast {
println("execution task")
}
}

(1..10).each { i ->
task("task_${i}") {
int index = i
if (index % 2 == 0) {
dependsOn("hello_world")
}

doLast {
println("executing task_${index}")
}
}
}

afterEvaluate {
println("after evaluate")
}

此脚本最后执行结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[min@TATEMIN-MB0:] wrapper-demo $ ./gradlew task_2

> Configure project : # Configuration阶段
configuration
after evaluate

> Task :hello_world # Execution阶段
execution task

> Task :task_2 # Execution阶段
executing task_2

BUILD SUCCESSFUL in 1s
2 actionable tasks: 2 executed

参考